<?php

namespace app\admin\model;

use app\common\model\MoneyLog;
use app\common\model\ScoreLog;
use Firebase\JWT\BeforeValidException;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\SignatureInvalidException;
use think\Model;
use fast\Tree;
use think\Db;

class User extends Model
{

    // 表名
    protected $name = 'user';
    public $prefix_new = 'fa_';
    // 自动写入时间戳字段
    protected $autoWriteTimestamp = 'int';
    // 定义时间戳字段名
    protected $createTime = 'createtime';
    protected $updateTime = 'updatetime';
    // 追加属性
    protected $append = [

    ];

    public function getOriginData()
    {
        return $this->origin;
    }

    //
    public function getUser_idTextAttr($value, $data=[])
    {
                if($value) {
            $nn=preg_split("/,/",$value);
        }else{
            $nn=array();
        }
        $s=array();
        for($i=0;$i<count($nn);$i++) {
            if($nn[$i]=="") continue;
            $r = Model("User")->get(array("id" => $nn[$i]));
            if($r) $s[]= $r['username'];
        }
        return join(",",$s);
    }

    public function getTeacher_idTextAttr($value, $data=[])
    {
                if($value) {
            $nn=preg_split("/,/",$value);
        }else{
            $nn=array();
        }
        $s=array();
        for($i=0;$i<count($nn);$i++) {
            if($nn[$i]=="") continue;
            $r = Model("User")->get(array("id" => $nn[$i]));
            if($r) $s[]= $r['username'];
        }
        return join(",",$s);
    }

    public function getstudent_idTextAttr($value, $data=[])
    {
                if($value) {
            $nn=preg_split("/,/",$value);
        }else{
            $nn=array();
        }
        $s=array();
        for($i=0;$i<count($nn);$i++) {
            if($nn[$i]=="") continue;
            $r = Model("User")->get(array("id" => $nn[$i]));
            if($r) $s[]= $r['username'];
        }
        return join(",",$s);
    }


    protected static function init()
    {
        self::beforeUpdate(function ($row) {
            $changed = $row->getChangedData();
            //如果有修改密码
            if (isset($changed['password'])) {
                if ($changed['password']) {
                    $salt = \fast\Random::alnum();
                    $row->password = \app\common\library\Auth::instance()->getEncryptPassword($changed['password'], $salt);
                    $row->salt = $salt;
                } else {
                    unset($row->password);
                }
            }
        });


        self::beforeUpdate(function ($row) {
            $changedata = $row->getChangedData();
            $origin = $row->getOriginData();
            if (isset($changedata['money']) && (function_exists('bccomp') ? bccomp($changedata['money'], $origin['money'], 2) !== 0 : (double)$changedata['money'] !== (double)$origin['money'])) {
                MoneyLog::create(['user_id' => $row['id'], 'money' => $changedata['money'] - $origin['money'], 'before' => $origin['money'], 'after' => $changedata['money'], 'memo' => '管理员变更金额']);
            }
            if (isset($changedata['score']) && (int)$changedata['score'] !== (int)$origin['score']) {
                ScoreLog::create(['user_id' => $row['id'], 'score' => $changedata['score'] - $origin['score'], 'before' => $origin['score'], 'after' => $changedata['score'], 'memo' => '管理员变更积分']);
            }
        });
    }

    public function getGenderList()
    {
        return ['1' => __('Male'), '0' => __('Female')];
    }

    public function getStatusList()
    {
        return ['normal' => __('Normal'), 'hidden' => __('Hidden')];
    }


    public function getPrevtimeTextAttr($value, $data)
    {
        $value = $value ? $value : ($data['prevtime'] ?? "");
        return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
    }

    public function getLogintimeTextAttr($value, $data)
    {
        $value = $value ? $value : ($data['logintime'] ?? "");
        return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
    }

    public function getJointimeTextAttr($value, $data)
    {
        $value = $value ? $value : ($data['jointime'] ?? "");
        return is_numeric($value) ? date("Y-m-d H:i:s", $value) : $value;
    }

    protected function setPrevtimeAttr($value)
    {
        return $value && !is_numeric($value) ? strtotime($value) : $value;
    }

    protected function setLogintimeAttr($value)
    {
        return $value && !is_numeric($value) ? strtotime($value) : $value;
    }

    protected function setJointimeAttr($value)
    {
        return $value && !is_numeric($value) ? strtotime($value) : $value;
    }

    protected function setBirthdayAttr($value)
    {
        return $value ? $value : null;
    }

    public function group()
    {
        return $this->belongsTo('UserGroup', 'group_id', 'id', [], 'LEFT')->setEagerlyType(0);
    }

    public function getGenderTextAttr($value, $data=array())
    {
        $s=array();
        $content=array(
            "1"=>"男",
            "2"=>"女",
            );
                if($value) {
            $nn=preg_split("/,/",$value);
        }else{
            $nn=array();
        }
        for($i=0;$i<count($nn);$i++) {
            if($nn[$i]=="") continue;
            if(isset($content[$nn[$i]])) $s[]= $content[$nn[$i]];
        }
        return join(",",$s);
    }

    public function getGroup_idTextAttr($value, $data=array())
    {
        $s=array();
        $content=array(
            "1"=>"学生",
            "2"=>"都是",
            "3"=>"管理员",
            "4"=>"领导",
            "5"=>"观察员",
            );
                if($value) {
            $nn=preg_split("/,/",$value);
        }else{
            $nn=array();
        }
        for($i=0;$i<count($nn);$i++) {
            if($nn[$i]=="") continue;
            if(isset($content[$nn[$i]])) $s[]= $content[$nn[$i]];
        }
        return join(",",$s);
    }

    public static function setApiToken($user, $expireTime = 604800) {
        $issuedAt = time();
        $student=model("\app\admin\model\Student")->where("student_id",$user["id"])->order("id desc")->find();
        if(!$student) {
            $student=array(
                'classes_id' => 0,
                'school_id' => 0,
            );
        }
        if(!$user["openid"]&&$user["mobile"]) {
            $user["openid"] = model("app\admin\model\User")->where("mobile",$user["mobile"])->where("openid is not null")->value("openid");
            if(!$user["openid"]) {
                model("app\admin\model\User")->where("mobile",$user["mobile"])->update(array('openid' => $user["openid"]));
            }
        }else{
            model("app\admin\model\User")->where("mobile",$user["mobile"])->update(array('openid' => $user["openid"]));
        }
        if(!$user["password"]&&$user["mobile"]) {
            $usertemp = model("app\admin\model\User")->where("mobile",$user["mobile"])->where("password is not null")->field("password,salt")->select();
            if(!$usertemp->isEmpty()) {
                $data=array();
                $data["password"] = $usertemp[0]["password"];
                $data["salt"] = $usertemp[0]["salt"];
                model("app\admin\model\User")->where("mobile",$user["mobile"])->where("password is null")->update($data);
            }
        }
        $expirationTime = $issuedAt + $expireTime;
        $payload = array(
            'iat' => $issuedAt,
            'exp' => $expirationTime,
            'uid' => $user["id"],
            'oid' => $user["openid"],
            'username' => $user["username"],
            'group_id' => $user["group_id"],
            'mobile' => $user["mobile"],
            'classes_id' => $student["classes_id"],
            'school_id' => $student["school_id"],
        );
        $token=JWT::encode($payload, "159456token!@#", 'HS256');
        header("Authorization: Bearer " . $token);
        return $token;
    }

    public static function getApiToken($token,$jump=true) {

        try {
            $token = str_replace('Bearer ', '', $token);
            $decoded = JWT::decode($token, new Key("159456token!@#", 'HS256'));
            return (array) $decoded;
        } catch (ExpiredException $e) {
            if($jump===true) {
                return array();
            }
             \app\common\controller\Api::errinfo('authorization 验证串无效或已过期');
        } catch (SignatureInvalidException $e) {
            if($jump===true) {
                return array();
            }
            \app\common\controller\Api::errinfo('authorization 验证串无效');
        } catch (BeforeValidException $e) {
            if($jump===true) {
                return array();
            }
            \app\common\controller\Api::errinfo('authorization 验证串无效');
        } catch (Exception $e) {
            if($jump===true) {
                return array();
            }
            \app\common\controller\Api::errinfo('authorization 验证串无效');
        }
        if($jump===true) {
            return array();
        }
        \app\common\controller\Api::errinfo('authorization 验证串无效');
        return false;
    }

    /**
     * 清除或失效指定用户的API Token
     * @param array $user 用户信息数组
     * @return bool 是否成功处理
     */
    public static function unsetApiToken($user) {
        header("Authorization: Bearer ");
        return true;
    }

    /**
     * 根据用户ID获取教室ID
     * @param int $userId 用户ID
     * @return array 教室ID数组
     */
    public function getClassroomByUserId($userId) {
        $classrooms = Db::name('eb_apply_form_item')
            ->where('user_id', $userId)
            ->column('classes_id');
        
        return $classrooms ?: [];
    }

    /**
     * 获取用户教学点树形结构（包含班级和学生）
     * 
     * @param int $adminId 管理员ID
     * @return array 树形结构数据
     */
    public static function getOcationTreeWithStudents($adminId = null)
    {
        if (!$adminId) {
            $adminId = session('think.admin.id');
        }
        
        // 获取管理员信息
        $admin = Db::name('admin')->where('id', $adminId)->find();
        if (!$admin) {
            return [];
        }
        
        $where = "where 1=1";
        $ocation_id = $admin['ocation_id'];
        
        $filter_ids = [];
        if ($ocation_id) {
            // 处理可能是逗号分隔的字符串
            $ocation_ids = is_string($ocation_id) ? explode(',', $ocation_id) : [$ocation_id];
            $filter_ids = [];
            foreach ($ocation_ids as $oid) {
                if (!empty($oid)) {
                    // 获取子节点
                    $children = self::getChildOcationIds($oid, false);
                    $filter_ids = array_merge($filter_ids, $children);
                    // 添加当前节点
                    $filter_ids[] = $oid;
                }
            }
            // 确保所有ID都是整数，并去重
            $filter_ids = array_unique(array_map('intval', $filter_ids));
        }
        
        if ($admin['group_id'] != 1) {
            $where .= " and city_id='{$admin['city_id']}'";
        }
        
        if ($admin['group_id'] == 2 || $admin['group_id'] == 8) {
            $where .= " and city_id='{$admin['city_id']}'";
        }
        
        // 获取所有数据用于构建树形结构
        $sql = "select id,pid,name,address,address_lv_octionlevel,status,weigh from fa_ocation $where order by weigh desc,id asc";
        $list = db()->query($sql);
        
        // 递归查找并补全所有祖先节点，确保根节点一定在$list中,然后找出$filter_ids的最近的上级父节点
        $idToPid = [];
        $allIds = array_column($list, 'id');
        foreach ($list as $item) {
            $idToPid[$item['id']] = $item['pid'];
        }
        
        if (!function_exists('fetchAncestors')) {
            function fetchAncestors($id, &$idToPid, &$allIds, &$list) {
                if (!isset($idToPid[$id])) {
                    $node = Db::name('ocation')->where('id', $id)->find();
                    if ($node) {
                        $idToPid[$node['id']] = $node['pid'];
                        $allIds[] = $node['id'];
                        $list[] = $node;
                        if ($node['pid']) {
                            fetchAncestors($node['pid'], $idToPid, $allIds, $list);
                        }
                    }
                }
            }
        }
        
        foreach ($list as $item) {
            fetchAncestors($item['id'], $idToPid, $allIds, $list);
        }
        
        $allIds = array_unique($allIds);
        $list = array_values(array_column($list, null, 'id'));
        
        // 找到最近的有效父节点作为根节点
        if (!function_exists('findNearestCommonParent')) {
            function findNearestCommonParent($ids, $idToPid) {
                if (empty($ids)) {
                    return null;
                }
                
                // 构建每个节点到根的路径
                $paths = [];
                foreach ($ids as $id) {
                    $path = [];
                    $currentId = $id;
                    while (isset($idToPid[$currentId]) && $idToPid[$currentId]) {
                        $path[] = $currentId;
                        $currentId = $idToPid[$currentId];
                    }
                    $path[] = $currentId; // 添加根节点
                    $paths[$id] = array_reverse($path);
                }
                
                // 如果只有一个节点，直接返回其父节点
                if (count($paths) === 1) {
                    $path = reset($paths);
                    return count($path) > 1 ? $path[count($path) - 2] : $path[0];
                }
                
                // 找到所有路径中最短的长度
                $minLen = min(array_map('count', $paths));
                
                // 从最后一个共同祖先向下查找，直到找到第一个分叉点的父节点
                $lastCommon = null;
                for ($i = 0; $i < $minLen; $i++) {
                    $firstPath = reset($paths);
                    $val = $firstPath[$i];
                    $isCommon = true;
                    
                    foreach ($paths as $path) {
                        if ($path[$i] !== $val) {
                            $isCommon = false;
                            break;
                        }
                    }
                    
                    if (!$isCommon) {
                        // 返回最后一个共同节点
                        return $lastCommon;
                    }
                    $lastCommon = $val;
                }
                
                // 如果所有节点都在同一条路径上，返回最深的共同节点
                return $lastCommon;
            }
        }
        
        $rootId = findNearestCommonParent($filter_ids, $idToPid);
        if (!$rootId) {
            $rootId = $admin['ocation_id'];
        }
        
        // 使用Tree类构建树形结构
        $tree = Tree::instance();
        $tree->init($list, 'pid');
        
        if ($rootId) {
            // 获取所有父级节点
            $parentNodes = $tree->getParents($rootId, false);
            // 如果需要包含父教学点信息，获取完整树结构
            if ($rootId > 0) {
                // 方案3：获取父节点及其所有子节点
                $currentNode = Db::name('ocation')->where('id', $rootId)->find();
                if ($currentNode && $currentNode['pid'] > 0) {
                    // 如果有父节点，以父节点为根获取树结构
                    $treeList = $tree->getTreeList($tree->getTreeArray($currentNode['pid']), 'name');
                } else {
                    // 如果没有父节点，以当前节点为根
                    $treeList = $tree->getTreeList($tree->getTreeArray($rootId), 'name');
                }
            } else {
                // rootId为0时，获取完整树结构
                $treeList = $tree->getTreeList($tree->getTreeArray(0), 'name');
            }
        } else {
            $treeList = [];
        }
        
        // 为每个节点添加层级信息
        foreach ($treeList as $key => &$item) {
            // 计算层级深度
            $level = substr_count($item['name'], '&nbsp;') / 4; // 每级4个&nbsp;
            $item['level'] = (int)$level; // 明确转换为整数，避免浮点数精度问题
            
            // 清理name中的HTML标签，保留纯文本
            $item['name'] = strip_tags(str_replace('&nbsp;', '', $item['name']));
            
            // 添加parent字段
            $item['parent'] = (isset($item['pid']) && $item['pid'] != 0) ? $item['pid'] : '#';
            
            // 计算是否有子节点
            $childCount = 0;
            foreach ($list as $child) {
                if ($child['pid'] == $item['id']) {
                    $childCount++;
                }
            }
            
            // 根据需求：当address_lv_octionlevel为3或4时，查询该教学点下的班级学生作为子节点
            if($item['address_lv_octionlevel']==4||$item['address_lv_octionlevel']==3){
                // 查询该教学点下的班级
                $classes = Db::name('eb_classes')
                    ->where('ocation_id', $item['id'])
                    ->where('status', 1)
                    ->select();
                
                // 为每个班级添加学生子节点
                foreach($classes as $class){
                    // 查询班级下的学生
                    $students = Db::name('user')
                        ->alias('u')
                        ->join('eb_class_student cu', 'u.id = cu.student_id')
                        ->where('cu.classes_id', $class['id'])
                        ->where('u.status', 'normal')
                        ->field('u.id,u.username,u.nickname')
                        ->select();
                    
                    // 如果有学生，则标记该教学点有子节点
                    if(count($students) > 0){
                        $childCount = 1; // 标记有子节点
                        break;
                    }
                }
            }
            
            $item['haschild'] = $childCount;
            
            // 添加其他必要字段
            $item['type'] = $childCount > 0 ? 'folder' : 'file';
            $item['status'] = $item['status'] == 1 ? 'normal' : 'hidden';
        }
        
        // 新增：为address_lv_octionlevel为3或4的节点添加学生子节点
        $newTreeList = [];
        foreach ($treeList as $item) {
            // 添加原始节点
            $newTreeList[] = $item;
            
            // 如果是3级或4级教学点，添加学生子节点
            if ($item['address_lv_octionlevel'] == 3 || $item['address_lv_octionlevel'] == 4) {
                // 查询该教学点下的班级
                $classes = Db::name('eb_classes')
                    ->where('ocation_id', $item['id'])
                    ->where('status', 1)
                    ->select();
                
                // 为每个班级添加学生子节点
                foreach ($classes as $class) {
                    // 查询班级下的学生
                    $students = Db::name('user')
                        ->alias('u')
                        ->join('eb_class_student cu', 'u.id = cu.student_id')
                        ->where('cu.classes_id', $class['id'])
                        ->where('u.status', 'normal')
                        ->field('u.id,u.username,u.nickname')
                        ->select();
                    
                    // 为每个学生添加子节点
                    foreach ($students as $student) {
                        $newTreeList[] = [
                            'id' => 'student_' . $student['id'], // 使用特殊前缀避免ID冲突
                            'pid' => $item['id'],
                            // 修改学生姓名显示逻辑，优先显示昵称，没有昵称则显示用户名，都没有则显示"未命名用户"
                            'name' => "│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;├─ <span style='color:green'>".(!empty($student['nickname']) ? $student['nickname'] : (!empty($student['username']) ? $student['username'] : '未命名用户'))."</span>",
                            'level' => (int)($item['level'] + 1), // 明确转换为整数
                            'parent' => $item['id'],
                            'haschild' => 0,
                            'type' => 'file',
                            'status' => 'normal',
                            'address_lv_octionlevel' => 5, // 特殊标记为学生节点
                            // 保留其他必要字段
                            'address' => '',
                            'weigh' => 0
                        ];
                    }
                }
            }
        }
        
        return $newTreeList;
    }
    
    /**
     * 获取指定教学点的所有子教学点ID（递归）
     *
     * @param int $parentId 父教学点ID
     * @param bool $includeSelf 是否包含自己
     * @return array 所有子教学点ID数组
     */
    private static function getChildOcationIds($parentId, $includeSelf = false)
    {
        $childIds = [];
        // 支持逗号分隔字符串或数组
        if (is_string($parentId) && strpos($parentId, ',') !== false) {
            $parentId = explode(',', $parentId);
        }
        // 获取直接子教学点
        $directChildren = Db::name('ocation')
            ->where('pid', 'in', (array)$parentId)
            ->where('status', 1)
            ->column('id');
        foreach ($directChildren as $childId) {
            $childIds[] = $childId;
            // 递归获取子教学点的子教学点
            $grandChildIds = self::getChildOcationIds($childId);
            $childIds = array_merge($childIds, $grandChildIds);
        }
        if ($includeSelf) {
            foreach ((array)$parentId as $pid) {
                array_unshift($childIds, $pid);
            }
        }
        return $childIds;
    }

    /**
     * 获取用户列表数据（替代/user/user/lists API）
     * @param array $params 查询参数
     * @return array
     */
    public function getUserLists($params = [])
    {
        $where = "where 1=1 ";
        $page = isset($params['page']) ? (int)$params['page'] : 1;
        $pagesize = isset($params['pagesize']) ? (int)$params['pagesize'] : 50;
        
        // 搜索条件
        if (isset($params['q']) && $params['q'] != "") {
            $where .= " and (username like '%" . trim($params['q']) . "%' or mobile like '%" . trim($params['q']) . "%')";
        }
        
        // 用户组过滤
        if (isset($params["groupId"]) && $params["groupId"] != "") {
            $where .= " and (group_id=" . (int)$params['groupId'] . ")";
        } else {
            $where .= " and (group_id=1 or group_id=2 or group_id=3)";
        }
        
        // 查询用户列表
        $sql = "select id value, username text, mobile phone, group_id from fa_user $where limit " . (($page - 1) * $pagesize) . ",$pagesize";
        $list = db()->query($sql);
        
        // 根据args参数收集已选中的记录集
        $leftOptions = [];
        if (isset($params["args"])) {
            $args = $params["args"];
            $studentIds = (isset($args[0]) && $args[0] != "[]") ? $args[0] : "";
            $teacherIds = (isset($args[1]) && $args[1] != "[]") ? $args[1] : "";
            $managerIds = (isset($args[2]) && $args[2] != "[]") ? $args[2] : "";
            
            if (!empty($studentIds)) {
                $students = db()->query("select id value, username text, mobile phone, group_id from fa_user where id in (" . join(',', $studentIds) . ")");
                $leftOptions = array_merge($leftOptions, $students);
            }
            if (!empty($teacherIds)) {
                $teachers = db()->query("select id value, username text, mobile phone, group_id from fa_user where id in (" . join(',', $teacherIds) . ")");
                $leftOptions = array_merge($leftOptions, $teachers);
            }
            if (!empty($managerIds)) {
                $managers = db()->query("select id value, username text, mobile phone, group_id from fa_user where id in (" . join(',', $managerIds) . ")");
                $leftOptions = array_merge($leftOptions, $managers);
            }
        }
        
        // 过滤掉已选中的用户
        $leftValues = array_column($leftOptions, 'value');
        $list = array_filter($list, function ($item) use ($leftValues) {
            return !in_array($item['value'], $leftValues);
        });
        
        // 获取总数
        $total = db()->query("select count(1) as c from fa_user $where")[0];
        
        return [
            "list" => $list, 
            "total" => $total['c'], 
            "leftOptions" => $leftOptions
        ];
    }
}
